﻿using MudEngine.Runtime;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace MudEngine.Collections
{
    public class Mapping : Dictionary<Value, Value>
    {
        /// <summary>
        /// 
        /// </summary>
        private List<Value> indexedCollection = new List<Value>();
        /// <summary>
        /// 
        /// </summary>
        public Mapping()
            : base()
        {
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="capacity"></param>
        public Mapping(int capacity)
            : base(capacity)
        {
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public bool Contains(Value key)
        {
            return base.ContainsKey(key);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="key"></param>
        /// <param name="item"></param>
        public virtual new void Add(Value key, Value item)
        {
            this.AddAt(-1, key, item);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="index"></param>
        /// <param name="key"></param>
        /// <param name="item"></param>
        public virtual void AddAt(int index, Value key, Value item)
        {
            base.Add(key, item);

            if (index != -1)
                indexedCollection.Insert(index, key);
            else
                indexedCollection.Add(key);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="index"></param>
        public virtual void RemoveAt(int index)
        {
            if (index < 0 || index >= indexedCollection.Count)
                throw new IndexOutOfRangeException();

            Value key = indexedCollection[index];
            this.Remove(key);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="key"></param>
        public new void Remove(Value key)
        {
            indexedCollection.Remove(key);
            base.Remove(key);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="index"></param>
        /// <returns></returns>
        public Value GetKey(int index)
        {
            return indexedCollection[index];
        }
        /// <summary>
        /// 
        /// </summary>
        public new void Clear()
        {
            base.Clear();
            indexedCollection.Clear();
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="predicate"></param>
        /// <returns></returns>
        public int FindIndex(Predicate<Value> predicate)
        {
            return indexedCollection.FindIndex(predicate);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public new Value this[Value key]
        {
            get
            {
                if (!base.ContainsKey(key))
                    return Value.constI1_0;
                return base[key];
            }
            set
            {
                if (!base.ContainsKey(key))
                    this.Add(key, value);
                else
                    base[key] = value;
            }
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="index"></param>
        /// <returns></returns>
        public Value this[int index]
        {
            get
            {
                if (index < 0 || index >= this.Count)
                    return Value.constI1_0;
                return this[indexedCollection[index]];
            }
            set
            {
                this[indexedCollection[index]] = value;
            }
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="other"></param>
        /// <returns></returns>
        public bool Equals(Mapping other)
        {
            if (this == other)
                return true;
            if (other == null)
                return false;
            if (this.Count != other.Count)
                return false;

            var comparer = EqualityComparer<Value>.Default;

            foreach (KeyValuePair<Value, Value> kvp in this)
            {
                Value secondValue;
                if (!other.TryGetValue(kvp.Key, out secondValue))
                    return false;
                if (!comparer.Equals(kvp.Value, secondValue))
                    return false;
            }
            return true;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="startIndex"></param>
        /// <param name="endIndex"></param>
        /// <returns></returns>
        public Mapping Submap(int startIndex, int endIndex)
        {
            Mapping newMapping = new Mapping(endIndex - startIndex + 1);
            for (int i = startIndex; i <= endIndex && i < this.Count; i++)
                newMapping.Add(this.GetKey(i), this[i]);
            return newMapping;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            StringBuilder builder = new StringBuilder("([");
            bool first = true;
            foreach (Value key in this.Keys)
            {
                if (!first) builder.Append(", ");

                builder.AppendFormat("{0}:{1}", key, this[key].ToString());
                first = false;
            }
            builder.Append("])");
            return builder.ToString();
        }
    }
}
